//
//  Protocols.swift
//  cnswift-demo
//
//  Created by YangWei on 2021/1/3.
//  Copyright © 2021 YangWei. All rights reserved.
//

import UIKit

class Protocols: SWDBaseObject {
    
    class func run() {
        let protocols = Protocols()
        
        // 2 属性要求
        protocols.propertyRequirements()
        
        // 3 方法要求
        protocols.methodRequirements()
        
        // 4 异变方法要求
        protocols.mutatingMethodRequirements()
        
        // 5 构造器要求
        protocols.initializerRequirements()
        
        // 5.1 协议构造器要求的类实现
        protocols.classImplementationsOfProtocolInitializerRequirements()
        
        // 6 协议作为类型
        protocols.protocolsAsTypes()
        
        // 7 委托
        protocols.delegation()
        
        // 8 在扩展里添加协议遵循
        protocols.addingProtocolConformanceWithAnExtension()
        
        // 8.1 有条件地遵循协议
        protocols.conditionallyConformingToAProtocol()
        
        // 8.2 在扩展里声明采纳协议
        protocols.declaringProtocolAdoptionWithAnExtension()
        
        // 9 使用合成实现来采纳协议
        protocols.adoptingAProtocolUsingASynthesizedImplementation()
        
        // 10 协议类型的集合
        protocols.collectionsOfProtocolTypes()
        
        // 11 协议的继承
        protocols.protocolInheritance()
        
        // 13 协议合成
        protocols.protocolComposition()
        
        // 14 检查协议一致性
        protocols.checkingForProtocolConformance()
        
        // 15 可选的协议要求
        protocols.optionalProtocolRequirements()
        
        // 16 协议扩展
        protocols.protocolExtensions()
        
        // 16.1 提供默认实现
        protocols.providingDefaultImplementations()
        
        // 16.2 给协议扩展添加限制
        protocols.addingConstraintsToProtocolExtensions()
    }
    
    /// 2 属性要求
    func propertyRequirements() {
        let title = "2 属性要求";
        printStartLine(title)
        
        let john = Person(fullName: "John Appleseed")
        // john.fullName 为 "John Appleseed"
        print(john.fullName)
        
        let ncc1701 = Starship(name: "Enterprise", prefix: "USS")
        // ncc1701.fullName 为 "USS Enterprise"
        print(ncc1701.fullName)
        
        printEndLine(title)
    }
    
    /// 2 属性要求
    /// 一个遵循 FullyNamed 协议的简单结构体
    struct Person: FullyNamed {
        var fullName: String
    }
    
    /// 2 属性要求
    /// 一个更加复杂的类，采纳并遵循了 FullyNamed 协议
    class Starship: FullyNamed {
        var prefix: String?
        var name: String
        init(name: String, prefix: String? = nil) {
            self.name = name
            self.prefix = prefix
        }
        
        // 把 fullName 作为只读的计算属性来实现
        var fullName: String {
            return (prefix != nil ? prefix! + " " : "") + name
        }
    }
    
    /// 3 方法要求
    func methodRequirements() {
        let title = "3 方法要求";
        printStartLine(title)
        
        let generator = LinearCongruentialGenerator()
        print("Here's a random number: \(generator.random())")
        // 打印 “Here's a random number: 0.3746499199817101”
        print("And another one: \(generator.random())")
        // 打印 “And another one: 0.729023776863283”
        
        printEndLine(title)
    }
    
    /// 3 方法要求
    /// 实现了一个叫做 线性同余生成器（linear congruential generator） 的伪随机数算法
    class LinearCongruentialGenerator: RandomNumberGenerator {
        var lastRandom = 42.0
        let m = 139968.0
        let a = 3877.0
        let c = 29573.0
        func random() -> Double {
            lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m))
            return lastRandom / m
        }
    }
    
    /// 4 异变方法要求
    func mutatingMethodRequirements() {
        let title = "4 异变方法要求";
        printStartLine(title)
        
        var lightSwitch = OnOffSwitch.off
        print(lightSwitch)
        lightSwitch.toggle()
        // lightSwitch 现在的值为 .on
        print(lightSwitch)
        
        printEndLine(title)
    }
    
    /// 4 异变方法要求
    /// 枚举在两种状态之间进行切换，用枚举成员 On 和 Off 表示
    enum OnOffSwitch: Togglable {
        case off, on
        // 被标记为 mutating，以满足 Togglable 协议的要求
        mutating func toggle() {
            switch self {
            case .off:
                self = .on
            case .on:
                self = .off
            }
        }
    }
    
    /// 5 构造器要求
    func initializerRequirements() {
        let title = "5 构造器要求";
        printStartLine(title)
        
        // 参看 protocol SomeProtocol 语法示例中：init(someParameter: Int) 方法定义
        
        printEndLine(title)
    }
    
    /// 5.1 协议构造器要求的类实现
    func classImplementationsOfProtocolInitializerRequirements() {
        let title = "5.1 协议构造器要求的类实现";
        printStartLine(title)
        
        /* 可以在遵循协议的类中实现构造器，无论是指定构造器，还是便利构造器，都必须为构造器实现标上 required 修饰符 */
        // 参看 class SomeClass: SomeProtocol 语法示例，required init(someParameter: Int) 方法定义
        // 参看 class SomeSubClass: SomeSuperClass, SomeProtocol 中 required override init() 方法的定义
        
        printEndLine(title)
    }
    
    /// 5.1 协议构造器要求的类实现
    class SomeClass: SomeProtocol {
        // required 修饰符确保所有子类必须提供此构造器实现，从而能遵循协议
        required init(someParameter: Int) {
            // 这里是构造器的实现部分
        }
        
        required init() {
            // 这里是构造器的实现部分
        }
    }
    
    /// 5.1 协议构造器要求的类实现
    class SomeSuperClass {
        init() {
            // 这里是构造器的实现部分
        }
    }
    
    /// 5.1 协议构造器要求的类实现
    class SomeSubClass: SomeSuperClass, SomeProtocol {
        required init(someParameter: Int) {
            // 这里是构造器的实现部分
        }
        
        // 因为遵循协议，需要加上 required
        // 因为继承自父类，需要加上 override
        required override init() {
            // 这里是构造器的实现部分
        }
    }
    
    /// 6 协议作为类型
    func protocolsAsTypes() {
        let title = "6 协议作为类型";
        printStartLine(title)
        
        let d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
        for _ in 1...5 {
            print("摇一个骰子，摇了： \(d6.roll())")
        }
        // 摇一个骰子，摇了： 3
        // 摇一个骰子，摇了： 5
        // 摇一个骰子，摇了： 4
        // 摇一个骰子，摇了： 5
        // 摇一个骰子，摇了： 4
        
        printEndLine(title)
    }
    
    /// 7 委托
    func delegation() {
        let title = "7 委托";
        printStartLine(title)
        
        // 记录游戏已经进行的轮数实例（实现协议：DiceGameDelegate）
        let tracker = DiceGameTracker()
        // 游戏实例（实现协议：DiceGame）
        let game = SnakesAndLadders()
        game.delegate = tracker
        game.play()
        // 开始一个新的蛇梯棋游戏
        // 这个游戏正在使用一个 6-面骰子
        // 摇一个骰子： 3
        // 摇一个骰子： 5
        // 摇一个骰子： 4
        // 摇一个骰子： 5
        // 这个游戏最后经历了 4 轮
        
        printEndLine(title)
    }
    
    /// 7 委托
    /// 基于之前控制流章节部分引入的蛇梯棋游戏的新版本
    /// 新版本使用 Dice 实例作为骰子
    /// 实现了 DiceGame 协议：提供相应的可读的 dice 属性、 play() 方法
    class SnakesAndLadders: DiceGame {
        // 棋盘方格最终位置
        let finalSquare = 25
        
        // DiceGame 协议要求属性
        // 在构造之后就不再改变，且协议只要求 dice 为可读的，因此将 dice 声明为常量属性。
        let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
        
        var square = 0
        var board: [Int]
        init() {
            board = Array(repeating: 0, count: finalSquare + 1)
            board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
            board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
        }
        
        // 实现了 DiceGameDelegate 协议（记录游戏的过程）
        // delegate 并不是游戏的必备条件，因此被定义为 DiceGameDelegate 类型的可选属性
        // 可选值，因此会被自动赋予初始值 nil
        var delegate: DiceGameDelegate?
        
        // DiceGame 协议要求方法
        func play() {
            // 若 delegate 为 nil，则调用方法会优雅地失败，并不会产生错误。
            // 若 delegate 不为 nil，则方法能够被调用，并传递 SnakesAndLadders 实例作为参数。
            square = 0
            // 在游戏开始时
            delegate?.gameDidStart(self)
            gameLoop: while square != finalSquare {
                let diceRoll = dice.roll()
                // 新一轮开始时
                delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
                switch square + diceRoll {
                case finalSquare:
                    break gameLoop
                case let newSquare where newSquare > finalSquare:
                    continue gameLoop
                default:
                    square += diceRoll
                    square += board[square]
                }
            }
            // 游戏结束时
            delegate?.gameDidEnd(self)
        }
    }
    
    /// 7 委托
    /// 实现 DiceGameDelegate 协议要求的三个方法：用来记录游戏已经进行的轮数
    class DiceGameTracker: DiceGameDelegate {
        // 当游戏开始时，numberOfTurns 属性被赋值为 0，然后在每新一轮中递增，游戏结束后，打印游戏的总轮数
        var numberOfTurns = 0
        
        func gameDidStart(_ game: DiceGame) {
            // 从 game 参数获取游戏信息并打印。
            // game 参数是 DiceGame 类型而不是 SnakeAndLadders 类型
            // 在 gameDidStart(_:) 方法中只能访问 DiceGame 协议中的内容
            numberOfTurns = 0
            if game is SnakesAndLadders {
                // SnakeAndLadders 的方法可以在类型转换之后调用
                // 通过 is 操作符检查 game 是否为 SnakesAndLadders 类型的实例
                // 是，则打印出相应的消息
                print("开始一个新的蛇梯棋游戏")
            }
            // 无论当前进行的是何种游戏，game 遵循 DiceGame 协议
            // 可以确保 game 含有 dice 属性。
            // 可以通过传入的 game 参数来访问 dice 属性，进而打印出 dice 的 sides 属性的值
            print("这个游戏正在使用一个 \(game.dice.sides)-面骰子")
        }
        func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
            numberOfTurns += 1
            print("摇一个骰子： \(diceRoll)")
        }
        func gameDidEnd(_ game: DiceGame) {
            print("这个游戏最后经历了 \(numberOfTurns) 轮")
        }
    }
    
    /// 8 在扩展里添加协议遵循
    func addingProtocolConformanceWithAnExtension() {
        let title = "8 在扩展里添加协议遵循";
        printStartLine(title)
        
        // 所有 Dice 的实例都可以看做 TextRepresentable 类型：
        let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
        print(d12.textualDescription)
        // 一个 12-面的骰子
        
        let game = SnakesAndLadders()
        print(game.textualDescription)
        
        printEndLine(title)
    }
    
    /// 8.1 有条件地遵循协议
    func conditionallyConformingToAProtocol() {
        let title = "8.1 有条件地遵循协议";
        printStartLine(title)
        
        let d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
        let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
        let myDice = [d6, d12]
        print(myDice.textualDescription)
        // [一个 6-面的骰子, 一个 12-面的骰子]
        
        printEndLine(title)
    }
    
    /// 8.2 在扩展里声明采纳协议
    func declaringProtocolAdoptionWithAnExtension() {
        let title = "8.2 在扩展里声明采纳协议";
        printStartLine(title)
        
        let simonTheHamster = Hamster(name: "西蒙")
        let somethingTextRepresentable: TextRepresentable = simonTheHamster
        print(somethingTextRepresentable.textualDescription)
        // 一个名叫 西蒙 的仓鼠
        
        printEndLine(title)
    }
    
    /// 8.2 在扩展里声明采纳协议
    /// 仓鼠
    struct Hamster {
        var name: String
        var textualDescription: String {
            return "一个名叫 \(name) 的仓鼠"
        }
    }
    
    /// 9 使用合成实现来采纳协议
    func adoptingAProtocolUsingASynthesizedImplementation() {
        let title = "9 使用合成实现来采纳协议";
        printStartLine(title)
        
        let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
        let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
        if twoThreeFour == anotherTwoThreeFour {
            print("这两个向量是相等的")
        }
        
        let levels = [SkillLevel.intermediate, SkillLevel.beginner,
                      SkillLevel.expert(stars: 5), SkillLevel.expert(stars: 3)]
        for level in levels.sorted() {
            print(level)
        }
        // 打印 "beginner"
        // 打印 "intermediate"
        // 打印 "expert(stars: 3)"
        // 打印 "expert(stars: 5)"
        
        /*  */
        let level5Expert = SkillLevel.expert(stars: 5)
        let level3Expert = SkillLevel.expert(stars: 3)
        if level5Expert == level3Expert {
            print("这两个专家水平一样")
        } else {
            print("这两个专家水平不一样")
        }
        
        printEndLine(title)
    }
    
    /// 9 使用合成实现来采纳协议
    /// 定义一个三维向量
    /// 由于 x、y 和 z 都是满足 Equatable 的类型，Vector3D 可以得到连等判断的合成实现
    struct Vector3D: Equatable {
        var x = 0.0, y = 0.0, z = 0.0
    }
    
    /// 9 使用合成实现来采纳协议
    /// 技能等级
    enum SkillLevel: Comparable {
        /// 初学者（beginner）
        case beginner
        /// 中级（intermediate）
        case intermediate
        /// 专家（expert）：专家等级会由额外的星级（stars）来进行排名
        case expert(stars: Int)
    }
    
    /// 10 协议类型的集合
    func collectionsOfProtocolTypes() {
        let title = "10 协议类型的集合";
        printStartLine(title)
        
        let game = SnakesAndLadders()
        let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
        let simonTheHamster = Hamster(name: "西蒙")
        
        // 创建了一个元素类型为 TextRepresentable 的数组：
        let things: [TextRepresentable] = [game, d12, simonTheHamster]

        // 遍历 things 数组，并打印每个元素的文本表示：
        for thing in things {
            print(thing.textualDescription)
        }
        // 一个终点为 25 的方格蛇梯棋盘游戏
        // 一个 12-面的骰子
        // 一个名叫 西蒙 的仓鼠
        
        printEndLine(title)
    }
    
    /// 11 协议的继承
    func protocolInheritance() {
        let title = "11 协议的继承";
        printStartLine(title)
        
        // 参看 PrettyTextRepresentable 协议的定义
        let game = SnakesAndLadders()
        print(game.prettyTextualDescription)
        // 一个终点为 25 的方格蛇梯棋盘游戏:
        // ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○
        
        printEndLine(title)
    }
    
    /// 13 协议合成
    func protocolComposition() {
        let title = "13 协议合成";
        printStartLine(title)
        
        /* 将 Named 和 Aged 两个协议按照上述语法组合成一个协议，作为函数参数的类型 */
        let birthdayPerson = PersonV2(name: "马尔科姆", age: 21)
        wishHappyBirthday(to: birthdayPerson)
        // 生日快乐, 马尔科姆, 你已 21 岁了!
        
        /* 将 Location 类和前面的 Named 协议进行组合 */
        // beginConcert(in:) 函数接受一个类型为 Location & Named 的参数
        // “任何 Location 的子类，并且遵循 Named 协议”。City 满足这样的条件
        let seattle = City(name: "西雅图", latitude: 47.6, longitude: -122.3)
        beginConcert(in: seattle)
        // 你好, 西雅图!
        
        printEndLine(title)
    }
    
    /// 13 协议合成
    /// Person 结构体采纳了两个协议：Named、Aged
    struct PersonV2: Named, Aged {
        var name: String
        var age: Int
    }
    
    /// 13 协议合成
    /// 参数 celebrator 的类型为 Named & Aged
    /// 意味“任何同时遵循 Named 和 Aged 的协议”
    func wishHappyBirthday(to celebrator: Named & Aged) {
        print("生日快乐, \(celebrator.name), 你已 \(celebrator.age) 岁了!")
    }
    
    /// 13 协议合成
    class Location {
        var latitude: Double
        var longitude: Double
        init(latitude: Double, longitude: Double) {
            self.latitude = latitude
            self.longitude = longitude
        }
    }
    
    /// 13 协议合成
    class City: Location, Named {
        var name: String
        init(name: String, latitude: Double, longitude: Double) {
            self.name = name
            super.init(latitude: latitude, longitude: longitude)
        }
    }
    
    /// 13 协议合成
    /// 开始音乐会
    func beginConcert(in location: Location & Named) {
        print("你好, \(location.name)!")
    }
    
    /// 14 检查协议一致性
    func checkingForProtocolConformance() {
        let title = "14 检查协议一致性";
        printStartLine(title)
        
        // 使用字面量初始化
        let objects: [AnyObject] = [
            Circle(radius: 2.0),    // 一个 radius 为 2 的 Circle 的实例
            Country(area: 243_610),    // 一个保存了英国国土面积的 Country 实例
            Animal(legs: 4)    // 一个 legs 为 4 的 Animal 实例
        ]
        
        for object in objects {
            if let objectWithArea = object as? HasArea {
                print("面积是 \(objectWithArea.area)")
            } else {
                print("这个东西还不能算出面积")
            }
        }
        // 面积是 12.5663708
        // 面积是 243610.0
        // 这个东西还不能算出面积
        
        printEndLine(title)
    }
    
    /// 14 检查协议一致性
    class Circle: HasArea {
        let pi = 3.1415927
        var radius: Double
        //  area 属性实现为基于存储型属性 radius 的计算型属性
        var area: Double { return pi * radius * radius }
        init(radius: Double) { self.radius = radius }
    }

    /// 14 检查协议一致性
    class Country: HasArea {
        // area 属性实现为存储型属性
        var area: Double
        init(area: Double) { self.area = area }
    }

    /// 14 检查协议一致性
    /// 不遵循 HasArea 协议
    class Animal {
        var legs: Int
        init(legs: Int) { self.legs = legs }
    }
    
    /// 15 可选的协议要求
    func optionalProtocolRequirements() {
        let title = "15 可选的协议要求";
        printStartLine(title)
        
        // 使用 ThreeSource 的实例作为 Counter 实例的数据源：
        let counter = Counter()
        counter.dataSource = ThreeSource()
        for _ in 1...4 {
            // 从 0 开始，每次 +3
            counter.increment()
            print(counter.count)
        }
        // 3
        // 6
        // 9
        // 12
        
        /*
        使用 TowardsZeroSource 实例将 Counter 实例来从 -4 增加到 0。
        一旦增加到 0，数值便不会再有变动：
        */
        counter.count = -4
        counter.dataSource = TowardsZeroSource()
        for _ in 1...5 {
            // 从 -4 逐渐 +1 变成 0
            counter.increment()
            print(counter.count)
        }
        // -3
        // -2
        // -1
        // 0
        // 0
        
        printEndLine(title)
    }
    
    /// 15 可选的协议要求
    /// 整数计数
    class Counter {
        // 变量属性 count 存储当前值
        var count = 0
        // 含有 CounterDataSource? 类型的可选属性 dataSource
        var dataSource: CounterDataSource?
        // 每次调用该方法的时候，将会增加 count 的值
        func increment() {
            // 首先使用 increment(forCount:) 方法来得到每次的增量
            // 使用可选链式调用来调用increment(forCount:)，将当前的 count 值作为参数传入
            // 两层可选链式调用
            if let amount = dataSource?.increment?(forCount: count) {
                count += amount
            } else if let amount = dataSource?.fixedIncrement {
                count += amount
            }
        }
    }
    
    /// 15 可选的协议要求
    /// 遵循 CounterDataSource 协议：实现了可选属性 fixedIncrement
    class ThreeSource: NSObject, CounterDataSource {
        let fixedIncrement = 3
    }
    
    /// 15 可选的协议要求
    /// 使值逐渐 -> 0，最后变为 0
    @objc class TowardsZeroSource: NSObject, CounterDataSource {
        func increment(forCount count: Int) -> Int {
            if count == 0 {
                return 0
            } else if count < 0 {
                return 1
            } else {
                return -1
            }
        }
    }
    
    /// 16 协议扩展
    func protocolExtensions() {
        let title = "16 协议扩展";
        printStartLine(title)
        
        // 通过协议扩展，所有遵循协议类型，都能自动获得这个扩展所增加的方法实现而无需任何额外修改：
        let generator = LinearCongruentialGenerator()
        print("这是一个随机数字: \(generator.random())")
        // 这是一个随机数字: 0.3746499199817101
        print("这是一个随机布尔值: \(generator.randomBool())")
        // 这是一个随机布尔值: true
        
        printEndLine(title)
    }
    
    /// 16.1 提供默认实现
    func providingDefaultImplementations() {
        let title = "16.1 提供默认实现";
        printStartLine(title)
        
        /*
         如果注释 extension Protocols.SnakesAndLadders: PrettyTextRepresentable 中关于 var prettyTextualDescription: String 的实现，代码将会执行 extension PrettyTextRepresentable 中定义的默认实现
         */
        let game = SnakesAndLadders()
        print(game.prettyTextualDescription)
        // 一个终点为 25 的方格蛇梯棋盘游戏:
        // ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○
        
        printEndLine(title)
    }
    
    /// 16.2 给协议扩展添加限制
    func addingConstraintsToProtocolExtensions() {
        let title = "16.2 给协议扩展添加限制";
        printStartLine(title)
        
        /* 两个整数数组，一个数组的所有元素都是一样的，另一个不一样 */
        let equalNumbers = [100, 100, 100, 100, 100]
        let differentNumbers = [100, 100, 200, 100, 200]

        // 数组遵循 Collection 而且整数遵循 Equatable
        // equalNumbers 和 differentNumbers 都可以使用 allEqual() 方法

        print(equalNumbers.allEqual())
        // true
        print(differentNumbers.allEqual())
        // false
        
        printEndLine(title)
    }
    
    func test() {
        
        // 给协议扩展添加限制
        let equalNumbers = [100, 100, 100, 100, 100]
        let differentNumbers = [100, 100, 200, 100, 200]
        print(equalNumbers.allEqual())
        print(differentNumbers.allEqual())
    }
}

/// 2 属性要求
/// 表示：任何遵循 FullyNamed 的类型，都必须有一个可读的 String 类型的实例属性 fullName
protocol FullyNamed {
    var fullName: String { get }
}

/// 3 方法要求
/// 定义一个含有一个实例方法的协议
/// 要求遵循协议的类型必须拥有一个名为 random， 返回值类型为 Double 的实例方法（假设返回值是从 0.0 到（但不包括）1.0）
/// 不关心每一个随机数是怎样生成的，只要求必须提供一个随机数生成器
protocol RandomNumberGenerator {
    func random() -> Double
}

/// 4 异变方法要求
protocol Togglable {
    // 改变实例属性，从而切换遵循该协议类型的实例的状态
    mutating func toggle()
}

/// 4 异变方法要求
/// 枚举在两种状态之间进行切换，用枚举成员 On 和 Off 表示
enum OnOffSwitch: Togglable {
    case off, on
    // 被标记为 mutating，以满足 Togglable 协议的要求
    mutating func toggle() {
        switch self {
        case .off:
            self = .on
        case .on:
            self = .off
        }
    }
}

/// 5 构造器要求
/// 5.1 协议构造器要求的类实现
protocol SomeProtocol {
    // 5 构造器要求
    init(someParameter: Int)
    
    // 5.1 协议构造器要求的类实现
    init()
}

/// 6 协议作为类型
/// 代表桌游中拥有 N 个面的骰子
class Dice {
    // 整型 表示骰子有几个面
    let sides: Int
    
    // 为骰子提供一个随机数生成器，生成随机点数
    // 任何遵循了 RandomNumberGenerator 协议的类型的实例都可以赋值给 generator
    let generator: RandomNumberGenerator
    
    // 构造器，用来设置初始状态
    // 创建 Dice 实例时，可以传入任何遵循 RandomNumberGenerator 协议实例给 generator
    init(sides: Int, generator: RandomNumberGenerator) {
        self.sides = sides
        self.generator = generator
    }
    
    // 模拟骰子的面值
    func roll() -> Int {
        // 先调用 generator 的 random() 方法来生成一个 [0.0,1.0) 区间内的随机数，然后使用这个随机数生成正确的骰子面值
        // generator 遵循 RandomNumberGenerator 协议，可以确保它有个 random() 方法可供调用
        return Int(generator.random() * Double(sides)) + 1
    }
}

/// 7 委托
/// DiceGame 协议可以被任意涉及骰子的游戏遵循
protocol DiceGame {
    var dice: Dice { get }
    func play()
}

/// 7 委托
/// DiceGameDelegate 协议可以被任意类型遵循
/// 提供了三个方法追踪游戏过程：分别在游戏开始时，新一轮开始时，以及游戏结束时被调用。
protocol DiceGameDelegate {
    // 在游戏开始时
    func gameDidStart(_ game: DiceGame)
    // 新一轮开始时
    func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
    // 游戏结束时
    func gameDidEnd(_ game: DiceGame)
}

/// 8 在扩展里添加协议遵循
/// 任何想要通过文本表示一些内容的类型都可以实现该协议
/// 表示的内容可以是实例本身的描述，也可以是实例当前状态的文本描述
protocol TextRepresentable {
    var textualDescription: String { get }
}

/// 8 在扩展里添加协议遵循
/// 可以通过扩展，令先前的 Dice 类扩展遵循 TextRepresentable 协议
/// 通过扩展遵循并采纳协议，和在原始定义中遵循并符合协议的效果完全相同
extension Dice: TextRepresentable {
    var textualDescription: String {
        return "一个 \(sides)-面的骰子"
    }
}

/// 8 在扩展里添加协议遵循
extension Protocols.SnakesAndLadders: TextRepresentable {
    var textualDescription: String {
        return "一个终点为 \(finalSquare) 的方格蛇梯棋盘游戏"
    }
}

/// 8.1 有条件地遵循协议
/// 只要在存储遵循 TextRepresentable 协议的元素时，就遵循 TextRepresentable 协议
extension Array: TextRepresentable where Element: TextRepresentable {
    var textualDescription: String {
        let itemsAsText = self.map { $0.textualDescription }
        return "[" + itemsAsText.joined(separator: ", ") + "]"
    }
}

/// 8.2 在扩展里声明采纳协议
extension Protocols.Hamster: TextRepresentable {}

/// 11 协议的继承
/// PrettyTextRepresentable 协议继承了 TextRepresentable 协议
/// 任何遵循 PrettyTextRepresentable 协议的类型在满足该协议的要求时
/// 也必须满足 TextRepresentable 协议的要求
protocol PrettyTextRepresentable: TextRepresentable {
    // 额外要求遵循协议的类型提供一个返回值为 String 的 prettyTextualDescription 属性
    var prettyTextualDescription: String { get }
}

/// 11 协议的继承
/// 扩展 SnakesAndLadders，使其遵循并符合 PrettyTextRepresentable 协议
extension Protocols.SnakesAndLadders: PrettyTextRepresentable {
    // 遵循 PrettyTextRepresentable 提供要求的 prettyTextualDescription 属性。
    var prettyTextualDescription: String {
        var output = textualDescription + ":\n"
        
        // 遍历数组中的元素，拼接一个几何图形来表示每个棋盘方格的内容：
        for index in 1...finalSquare {
            switch board[index] {
            case let ladder where ladder > 0:
                // 当从数组中取出的元素的值大于 0 时，用 ▲ 表示。
                output += "▲ "
            case let snake where snake < 0:
                // 当从数组中取出的元素的值小于 0 时，用 ▼ 表示。
                output += "▼ "
            default:
                // 当从数组中取出的元素的值等于 0 时，用 ○ 表示。
                output += "○ "
            }
        }
        return output
    }
}

/// 13 协议合成
/// Named 协议包含 String 类型的 name 属性
protocol Named {
    var name: String { get }
}

/// 13 协议合成
/// Aged 协议包含 Int 类型的 age 属性
protocol Aged {
    var age: Int { get }
}

/// 14 检查协议一致性
/// 有面积
protocol HasArea {
    // 定义一个 Double 类型的可读属性 area
    var area: Double { get }
}

/// 15 可选的协议要求
/// 整数计数外部数据源定义
@objc protocol CounterDataSource {
    // 可选方法
    @objc optional func increment(forCount count: Int) -> Int
    // 可选属性
    @objc optional var fixedIncrement: Int { get }
}

/// 16 协议扩展
/// 扩展 RandomNumberGenerator 协议来提供 randomBool() 方法
extension RandomNumberGenerator {
    // 使用协议中定义的 random() 方法来返回一个随机的 Bool 值
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

/// 16.1 提供默认实现
extension PrettyTextRepresentable  {
    // 为 PrettyTextRepresentable 提供一个默认的 prettyTextualDescription 属性
    // 简单地返回 textualDescription 属性的值
    var prettyTextualDescription: String {
        return textualDescription
    }
}

/// 16.2 给协议扩展添加限制
/// 扩展系统 Collection 协议：适用于集合中的元素遵循了 Equatable 协议的情况
/// 通过限制集合元素遵循 Equatable 协议， 作为标准库的一部分
/// 可以使用 == 和 != 操作符来检查两个元素的等价性和非等价性
extension Collection where Element: Equatable {
    func allEqual() -> Bool {
        for element in self {
            if element != self.first {
                return false
            }
        }
        return true
    }
}
